Render this report with
~/spinal_cord_paper/scripts/Gg_devel_scWGCNA_module_analysis_render.sh.
library(Seurat)
Attaching SeuratObject
library(WGCNA)
Loading required package: dynamicTreeCut
Loading required package: fastcluster
Attaching package: ‘fastcluster’
The following object is masked from ‘package:stats’:
hclust
Attaching package: ‘WGCNA’
The following object is masked from ‘package:stats’:
cor
library(tidyr)
library(ggplot2)
Use suppressPackageStartupMessages() to eliminate package startup messages
library(stringr)
library(patchwork)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ tibble 3.1.8 ✔ dplyr 1.0.10
✔ readr 1.4.0 ✔ forcats 0.5.1
✔ purrr 0.3.4
── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:patchwork’:
align_plots
library(pheatmap)
library(gridExtra)
Attaching package: ‘gridExtra’
The following object is masked from ‘package:dplyr’:
combine
source("~/Neuraltube/scripts/heatmap4.R")
Load individual seurat and test WGCNA data
The individual data sets are the Day 5 (Gg_D05_ctrl_seurat_070323),
Day 7 (Gg_D07_ctrl_seurat_070323), and Day 10 (Gg_ctrl_1_seurat_070323)
chicken spinal cord sets. The test WGCNA data are the modules calculated
on the integrated data set of all three stages.
se_path <- c("Gg_D05_ctrl_seurat_070323",
"Gg_D07_ctrl_seurat_070323",
"Gg_ctrl_1_seurat_070323")
clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv") %>%
rename(broad = broad_cluster) %>%
select(-marker)
Order of the broad clusters for plotting purposes.
broad_order <- c("progenitors",
"FP",
"RP",
"FP/RP",
"neurons",
"OPC",
"MFOL",
"pericytes",
"microglia",
"blood",
"vasculature"
)
my.ses <- list()
col_table <- list()
ord_levels <- list()
for (i in seq(se_path)) {
# load the data sets
my.se <- readRDS(paste0("~/spinal_cord_paper/data/", se_path[i], ".rds"))
annot <- read.csv(list.files("~/spinal_cord_paper/annotations",
pattern = str_remove(se_path[i], "_seurat_\\d{6}"),
full.names = TRUE))
if(length(table(annot$number)) != length(table(my.se$seurat_clusters))) {
stop("Number of clusters must be identical!")
}
# rename for left join
annot <- annot %>%
mutate(fine = paste(fine, number, sep = "_")) %>%
mutate(number = factor(number, levels = 1:nrow(annot))) %>%
rename(seurat_clusters = number)
# cluster order for vln plots
ord_levels[[i]] <- annot$fine[order(match(annot$broad, broad_order))]
# create index for color coding
col_table[[i]] <- annot %>%
left_join(clust_col, by = "broad") %>%
select(c("fine", "color"))
# add cluster annotation to meta data
my.se@meta.data <- my.se@meta.data %>%
rownames_to_column("rowname") %>%
left_join(annot, by = "seurat_clusters") %>%
mutate(fine = factor(fine, levels = annot$fine)) %>%
column_to_rownames("rowname")
my.ses[[i]] <- my.se
}
names(my.ses) <- c("D05", "D07", "D10")
names(col_table) <- c("D05", "D07", "D10")
names(ord_levels) <- c("D05", "D07", "D10")
rm(my.se, annot)
# The reference WGCNA data. We can have several, if we want to test many at the same time
WGCNA_data = list()
WGCNA_data[[1]] = readRDS("~/spinal_cord_paper/output/Gg_devel_int_scWGCNA_250723.rds")
my.wsub =list()
my.wsub[[1]]= c(1:22)
# the name of each sample, as they appear in my.files and in the metadata of the combined object
my.samplenames = c("D05", "D07", "D10")
# The subset of clusters in each of the corresponding samples
my.subset=list(c(1:24),
c(1:26),
c(1:22))
# This is just to add a little bit more sense to the modules, so that we don't get just a color. Corresponds to WGCNA_data
my.modulenames = list()
my.modulenames[[1]] = c(1:22)
#Subset the seurat objects if needed as defined above
for (i in 1:length(my.ses)) {
my.ses[[i]] = subset(my.ses[[i]], idents = my.subset[[i]])
}
# Take only genes that are present in the all samples
my.dcs = list()
# We go trhough each WGCNA data
for(i in 1:length(WGCNA_data)) {
#We start with complete modules
my.dc = WGCNA_data[[i]]$dynamicCols
#Remove the few genes that are not found in the other datasets
my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[1]]@assays$RNA@data))]
my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[2]]@assays$RNA@data))]
my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[3]]@assays$RNA@data))]
my.dc = my.dc[which(my.dc %in% names(table(WGCNA_data[[i]]$dynamicCols))[my.wsub[[i]]])]
my.dcs[[i]] = my.dc
}
Module gene correlation
Here, we do a correlation matrix / heatmap, to see which cell
clusters group togheter. This helps us to make more detailed
dotplots.
This part of the script can still be used to compare several WGCNA
datasets in parallel.
# broad cluster color table
all_col <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1, 3)) %>%
mutate(sample_celltype = paste(sample, fine, sep = "_")) %>%
select(c("color", "sample_celltype", "sample"))
# take the normalized data, of each single-cell object.
my.datExpr = list()
# Go trough each seurat object
for (i in 1:length(my.ses)) {
my.datExpr[[i]] = data.frame(my.ses[[i]]@assays$RNA@data)
}
# Calculate, for every cell, in every sample, the average expression of each of the modules.
my.MEs = list()
# For each set of WGCNA modules
for (i in 1:length(WGCNA_data)) {
# Make a new list
my.MEs[[i]] = list()
# Populate with data frames from each seurat object
for (j in 1:length(my.ses)) {
# Data frame of average module expression, per cell.
my.MEs[[i]][[j]] = moduleEigengenes(t(my.datExpr[[j]][names(my.dcs[[i]]),]), colors = my.dcs[[i]])
}
}
#Calculate average of expression, per sample and cell cluster
my.modavg = list()
for (i in 1:length(WGCNA_data)) {
my.modavg[[i]] = list()
for (j in 1:length(my.ses)) {
#Make the mean per cell clusters
my.modavg[[i]][[j]] = aggregate(
my.MEs[[i]][[j]]$averageExpr,
list(my.ses[[j]]@meta.data$seurat_clusters),
mean
)
# Give the cell clusters meaningful names
rownames(my.modavg[[i]][[j]]) = paste0(
my.samplenames[j],
"_",
levels(my.ses[[j]]@meta.data$fine)[my.subset[[j]]]
)
}
}
#Gather data to plot
my.wgmat = list()
for (i in 1:length(WGCNA_data)) {
#bind the expression data into one dataframe
my.wgmat[[i]] = data.table::rbindlist(my.modavg[[i]])
#Get rid of the extra column
my.wgmat[[i]] = data.frame(my.wgmat[[i]][,-1])
#Restore the rownames
rownames(my.wgmat[[i]]) = unlist(sapply(my.modavg[[i]], rownames))
}
#Get a dataframe with annotations for all the samples and colors we need.
my.metam <- list()
for (i in seq(my.ses)) {
my.metam[[i]] <- my.ses[[i]][[]]
rownames(my.metam[[i]]) <- paste0(rownames(my.metam[[i]]), "_", i)
}
my.metam <- do.call(rbind, my.metam)
my.metam$orig.ident <- str_replace_all(my.metam$orig.ident, pattern = "Gg_D05_ctrl", "D05")
my.metam$orig.ident <- str_replace_all(my.metam$orig.ident, pattern = "Gg_D07_ctrl", "D07")
my.metam$orig.ident <- str_replace_all(my.metam$orig.ident, pattern = "Gg_ctrl_1", "D10")
# my.metam$sample_celltype = paste0(substr(my.metam$orig.ident,7,9),"_",my.metam$seurat_clusters)
my.metam$sample_celltype = paste0(my.metam$orig.ident, "_", my.metam$fine)
my.metam <- my.metam %>%
tibble::rownames_to_column(var = "cell_ID") %>%
dplyr::left_join(all_col, by = "sample_celltype") %>%
tibble::column_to_rownames(var = "cell_ID")
# get sample colors
my.colsm = c("grey", "grey30", "black")
names(my.colsm) <- c("D05", "D07", "D10")
#The colors for the samples and clusters. First the closest to the heatmap. Add a white space to easy the eye and make less confussing
my.heatcols =list()
for (i in 1:length(my.wgmat)) {
my.heatcols[[i]] = as.matrix(data.frame(
sample= as.character(my.colsm[match(all_col$sample, names(my.colsm))]),
"." = "white",
cluster= as.character(all_col$color[match(rownames(my.wgmat[[i]]), all_col$sample_celltype)]))
)
}
rm(my.datExpr, my.MEs, my.dcs)
spearman correlation heatmap
annotations
# names and colors for the heatmap annotation
annot_name <- data.frame(
"Celltypes" = all_col$sample_celltype,
"Sample" = all_col$sample,
row.names = all_col$sample_celltype)
pheat_col_table <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1,3)) %>%
mutate(fine = paste(sample, fine, sep = "_"))
# match color table with annotation
pheat_col_table <- pheat_col_table[match(annot_name$Celltypes, pheat_col_table$fine),]
annot_col <- list(
Celltypes = pheat_col_table$color,
Sample = c(D05 = "#A4A4A4",
D07 = "#515151",
D10 = "#000000")
)
names(annot_col[[1]]) <- annot_name$Celltypes

heatmap of module pseudobulk average expression

Load the integrated data set
The integrated data set on which the WGCNA is calculated. We plot it,
split by the original cell types from the three original samples.

Avg. module exp. by stage
tSNE DimPlots showing the average expression of each module by
stage.

AE over time
We plot the average expression of each module in the three stages and
the 5 broad cell type clusters present in all 3 stages.
mod_annot <- read.csv("~/spinal_cord_paper/annotations/Gg_devel_int_scWGCNA_module_annotation.csv") %>%
dplyr::mutate(module = str_replace_all(module, "\\d{1,2}\\_", "AE"))
meta <- list()
for (i in seq(my.ses)) {
meta[[i]] <- my.ses[[i]]@meta.data %>%
tibble::rownames_to_column("cell_ID") %>%
dplyr::mutate(cell_ID = paste0(cell_ID, "_", i)) %>%
dplyr::select(c("cell_ID", "broad"))
}
meta <- do.call(rbind, meta)
# mean average expression by stage
mean_AE <- avg.mod.eigengenes %>%
tibble::rownames_to_column("cell_ID") %>%
dplyr::mutate(stage = stringr::str_sub(cell_ID, -1)) %>%
dplyr::mutate(stage = factor(stage, levels = c(1:3), labels = c("D05", "D07", "D10"))) %>%
dplyr::left_join(meta, by = "cell_ID") %>%
tibble::column_to_rownames("cell_ID") %>%
tidyr::unite("stage_cl", stage, broad, sep = "_") %>%
dplyr::group_by(stage_cl) %>%
dplyr::summarise_each(mean) %>%
dplyr::ungroup() %>%
gather(key="module", value = "AE", -stage_cl) %>%
dplyr::left_join(mod_annot[, c(1,3)], by = "module") %>%
tidyr::separate("stage_cl", c("stage", "broad"), sep = "_", remove = FALSE)
Warning: `summarise_each_()` was deprecated in dplyr 0.7.0.
ℹ Please use `across()` instead.
ℹ The deprecated feature was likely used in the dplyr package.
Please report the issue at <https://github.com/tidyverse/dplyr/issues>.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
# %>%
# dplyr::filter(broad %in% c("progenitors", "neurons", "RP", "FP", "pericytes"))
labels_dotplot <- stringr::str_remove(modules_in_order, "^AE")
names(labels_dotplot) <- modules_in_order
plot_clusters <- c("progenitors", "neurons", "RP", "FP", "pericytes", "OPC", "MFOL", "microglia", "blood")
mean_mod <- ggplot(data = mean_AE,
aes(
x = stage,
y = AE,
color = factor(broad, levels = plot_clusters),
group = broad,
label = annotation
)
) +
geom_line() +
geom_point() +
scale_color_manual(values = clust_col$color[match(plot_clusters, clust_col$broad)]) +
theme_bw() +
# facet wrap with reordered factors
facet_wrap(vars(factor(module, levels = unique(mean_AE$module)[module_order])),
scales = "free_y",
nrow = 4,
ncol = 6) +
labs(color = "broad") +
ggtitle("Average module expression by stage")
plotly::ggplotly(mean_mod)
VlnPlots of avg. module exp. by stage and seurat cluster
colored by module


# reorder seurat clusters
for (i in seq(my.ses)) {
my.ses[[i]]$seurat_clusters <- factor(
my.ses[[i]]$seurat_clusters,
levels = levels(my.ses[[i]]$seurat_clusters)[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
)
}
vplots <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots[[1]]
vplots[[2]]
vplots[[3]]

colored by cell type

clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv")
vplots_id <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots_id[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
fill.by = "ident",
cols = col_table[[i]]$color[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots_id[[1]]
vplots_id[[2]]
vplots_id[[3]]
MN modules
For the figures we specifically select the two MN modules and plot
them as Vln plots.


avg. module exp. by stage
v1 <- VlnPlot(my.sec,
features = mods[module_order],
group.by = "orig.ident")
v1
pdf("~/spinal_cord_paper/figures/Fig_1_AE_by_stage.pdf", height = 20, width = 20)
v1
module GO terms barplots
# Load the top 50 (by limma::topGO) Go term table list from scWGCNA_Gg_devel_int.Rmd
goterms <- readRDS("~/spinal_cord_paper/output/Gg_devel_int_module_GOTerms_250723.rds")
# Load the devel module annotations
modules <- read.csv("~/spinal_cord_paper/annotations/Gg_devel_int_scWGCNA_module_annotation.csv")
# Set list names as module number and name (e.g. "1_black")
names(goterms) <- modules$module
plot_list <- list()
for (i in seq(goterms)) {
# Select the darkred module (motor neuron/transmembrane transport)
toplot <- goterms[[i]]
terms <- as.character(toplot$Term)
#Plot the barplot!
plot_list[[i]] <- ggplot(toplot, aes(x=Term, y=-log10(P.DE), fill=Ont)) +
geom_bar(stat="identity") +
scale_fill_manual(values = str_remove(names(goterms)[i], "^\\d{1,2}_")) +
theme(axis.text.x = element_text(angle = 90, hjust = 1),
axis.text.y = element_text(size = 5)) +
labs(title=paste0("Top GOTerm of module ", names(goterms)[i])) +
coord_flip() +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
theme_cowplot()
}
rm(toplot, terms)
names(plot_list) <- modules$module
# Export PDF
pdf("~/spinal_cord_paper/figures/GO_term_barplot_scWGCNA_Gg_devel_modules.pdf", width = 14, height = 7)
for (i in seq(goterms)) {
grid.arrange(plot_list[[i]])
}
dev.off()
# Date and time of Rendering
Sys.time()
sessionInfo()
LS0tCnRpdGxlOiAiRGV2ZWxfaW50IFdHQ05BIG1vZHVsZXMgZXhwcmVzc2lvbiBpbiBOVCBENSwgRDcsIGFuZCBEMTAgc2FtcGxlcyIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMDQuMDguMjAyMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDgKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClJlbmRlciB0aGlzIHJlcG9ydCB3aXRoIH4vc3BpbmFsX2NvcmRfcGFwZXIvc2NyaXB0cy9HZ19kZXZlbF9zY1dHQ05BX21vZHVsZV9hbmFseXNpc19yZW5kZXIuc2guCgpgYGB7ciBzZXR1cH0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoV0dDTkEpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShncmlkRXh0cmEpCnNvdXJjZSgifi9OZXVyYWx0dWJlL3NjcmlwdHMvaGVhdG1hcDQuUiIpCgpgYGAKCiMgTG9hZCBpbmRpdmlkdWFsIHNldXJhdCBhbmQgdGVzdCBXR0NOQSBkYXRhCgpUaGUgaW5kaXZpZHVhbCBkYXRhIHNldHMgYXJlIHRoZSBEYXkgNSAoR2dfRDA1X2N0cmxfc2V1cmF0XzA3MDMyMyksIERheSA3IChHZ19EMDdfY3RybF9zZXVyYXRfMDcwMzIzKSwgYW5kIERheSAxMCAoR2dfY3RybF8xX3NldXJhdF8wNzAzMjMpIGNoaWNrZW4gc3BpbmFsIGNvcmQgc2V0cy4gVGhlIHRlc3QgV0dDTkEgZGF0YSBhcmUgdGhlIG1vZHVsZXMgY2FsY3VsYXRlZCBvbiB0aGUgaW50ZWdyYXRlZCBkYXRhIHNldCBvZiBhbGwgdGhyZWUgc3RhZ2VzLgoKYGBge3IgZGF0YS1zZXRzfQpzZV9wYXRoIDwtIGMoIkdnX0QwNV9jdHJsX3NldXJhdF8wNzAzMjMiLAogICAgICAgICAgICAgIkdnX0QwN19jdHJsX3NldXJhdF8wNzAzMjMiLAogICAgICAgICAgICAgIkdnX2N0cmxfMV9zZXVyYXRfMDcwMzIzIikKCmNsdXN0X2NvbCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9icm9hZF9jbHVzdGVyX21hcmtlcl9jb2xvcnMuY3N2IikgJT4lIAogIHJlbmFtZShicm9hZCA9IGJyb2FkX2NsdXN0ZXIpICU+JSAKICBzZWxlY3QoLW1hcmtlcikKCmBgYAoKT3JkZXIgb2YgdGhlIGJyb2FkIGNsdXN0ZXJzIGZvciBwbG90dGluZyBwdXJwb3Nlcy4KCmBgYHtyIG9yZGVyaW5nfQpicm9hZF9vcmRlciA8LSBjKCJwcm9nZW5pdG9ycyIsCiAgICAgICJGUCIsCiAgICAgICJSUCIsCiAgICAgICJGUC9SUCIsCiAgICAgICJuZXVyb25zIiwKICAgICAgIk9QQyIsCiAgICAgICJNRk9MIiwKICAgICAgInBlcmljeXRlcyIsCiAgICAgICJtaWNyb2dsaWEiLAogICAgICAiYmxvb2QiLAogICAgICAidmFzY3VsYXR1cmUiCiAgICAgICkKYGBgCgoKYGBge3Igc2V1cmF0LW9iamVjdHMtYW5kLWFubm90YXRpb25zfQpteS5zZXMgPC0gbGlzdCgpCmNvbF90YWJsZSA8LSBsaXN0KCkKb3JkX2xldmVscyA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEoc2VfcGF0aCkpIHsKICAjIGxvYWQgdGhlIGRhdGEgc2V0cwogIG15LnNlIDwtIHJlYWRSRFMocGFzdGUwKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvIiwgc2VfcGF0aFtpXSwgIi5yZHMiKSkKICBhbm5vdCA8LSByZWFkLmNzdihsaXN0LmZpbGVzKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSBzdHJfcmVtb3ZlKHNlX3BhdGhbaV0sICJfc2V1cmF0X1xcZHs2fSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUpKQogIAogIGlmKGxlbmd0aCh0YWJsZShhbm5vdCRudW1iZXIpKSAhPSBsZW5ndGgodGFibGUobXkuc2Ukc2V1cmF0X2NsdXN0ZXJzKSkpIHsKICAgICBzdG9wKCJOdW1iZXIgb2YgY2x1c3RlcnMgbXVzdCBiZSBpZGVudGljYWwhIikKICB9CiAgCiAgIyByZW5hbWUgZm9yIGxlZnQgam9pbgogIGFubm90IDwtIGFubm90ICU+JSAKICAgIG11dGF0ZShmaW5lID0gcGFzdGUoZmluZSwgbnVtYmVyLCBzZXAgPSAiXyIpKSAlPiUgCiAgICBtdXRhdGUobnVtYmVyID0gZmFjdG9yKG51bWJlciwgbGV2ZWxzID0gMTpucm93KGFubm90KSkpICU+JSAKICAgIHJlbmFtZShzZXVyYXRfY2x1c3RlcnMgPSBudW1iZXIpIAogIAogICMgY2x1c3RlciBvcmRlciBmb3IgdmxuIHBsb3RzCiAgb3JkX2xldmVsc1tbaV1dIDwtIGFubm90JGZpbmVbb3JkZXIobWF0Y2goYW5ub3QkYnJvYWQsIGJyb2FkX29yZGVyKSldCiAgCiAgIyBjcmVhdGUgaW5kZXggZm9yIGNvbG9yIGNvZGluZwogIGNvbF90YWJsZVtbaV1dIDwtIGFubm90ICU+JQogICAgbGVmdF9qb2luKGNsdXN0X2NvbCwgYnkgPSAiYnJvYWQiKSAlPiUgCiAgICBzZWxlY3QoYygiZmluZSIsICJjb2xvciIpKQogIAogICMgYWRkIGNsdXN0ZXIgYW5ub3RhdGlvbiB0byBtZXRhIGRhdGEKICBteS5zZUBtZXRhLmRhdGEgPC0gbXkuc2VAbWV0YS5kYXRhICU+JSAKICAgIHJvd25hbWVzX3RvX2NvbHVtbigicm93bmFtZSIpICU+JSAKICAgIGxlZnRfam9pbihhbm5vdCwgYnkgPSAic2V1cmF0X2NsdXN0ZXJzIikgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBmYWN0b3IoZmluZSwgbGV2ZWxzID0gYW5ub3QkZmluZSkpICU+JSAKICAgIGNvbHVtbl90b19yb3duYW1lcygicm93bmFtZSIpCiAgCiAgbXkuc2VzW1tpXV0gPC0gbXkuc2UKCn0KCm5hbWVzKG15LnNlcykgPC0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQpuYW1lcyhjb2xfdGFibGUpIDwtIGMoIkQwNSIsICJEMDciLCAiRDEwIikKbmFtZXMob3JkX2xldmVscykgPC0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQoKcm0obXkuc2UsIGFubm90KQpgYGAKCmBgYHtyIGlucHV0IGRhdGEsIGVjaG89VFJVRX0KIyBUaGUgcmVmZXJlbmNlIFdHQ05BIGRhdGEuIFdlIGNhbiBoYXZlIHNldmVyYWwsIGlmIHdlIHdhbnQgdG8gdGVzdCBtYW55IGF0IHRoZSBzYW1lIHRpbWUKV0dDTkFfZGF0YSA9IGxpc3QoKQpXR0NOQV9kYXRhW1sxXV0gPSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL291dHB1dC9HZ19kZXZlbF9pbnRfc2NXR0NOQV8yNTA3MjMucmRzIikKbXkud3N1YiA9bGlzdCgpCm15LndzdWJbWzFdXT0gYygxOjIyKQoKIyB0aGUgbmFtZSBvZiBlYWNoIHNhbXBsZSwgYXMgdGhleSBhcHBlYXIgaW4gbXkuZmlsZXMgYW5kIGluIHRoZSBtZXRhZGF0YSBvZiB0aGUgY29tYmluZWQgb2JqZWN0Cm15LnNhbXBsZW5hbWVzID0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQoKIyBUaGUgc3Vic2V0IG9mIGNsdXN0ZXJzIGluIGVhY2ggb2YgdGhlIGNvcnJlc3BvbmRpbmcgc2FtcGxlcwpteS5zdWJzZXQ9bGlzdChjKDE6MjQpLAogICAgICAgICAgICAgICBjKDE6MjYpLAogICAgICAgICAgICAgICBjKDE6MjIpKQoKIyBUaGlzIGlzIGp1c3QgdG8gYWRkIGEgbGl0dGxlIGJpdCBtb3JlIHNlbnNlIHRvIHRoZSBtb2R1bGVzLCBzbyB0aGF0IHdlIGRvbid0IGdldCBqdXN0IGEgY29sb3IuIENvcnJlc3BvbmRzIHRvIFdHQ05BX2RhdGEKbXkubW9kdWxlbmFtZXMgPSBsaXN0KCkKbXkubW9kdWxlbmFtZXNbWzFdXSA9IGMoMToyMikKCmBgYAoKYGBge3IgcHJlLXByb2Nlc3MsIGVjaG89VFJVRX0KCiNTdWJzZXQgdGhlIHNldXJhdCBvYmplY3RzIGlmIG5lZWRlZCBhcyBkZWZpbmVkIGFib3ZlCmZvciAoaSBpbiAxOmxlbmd0aChteS5zZXMpKSB7CiAgbXkuc2VzW1tpXV0gPSBzdWJzZXQobXkuc2VzW1tpXV0sIGlkZW50cyA9IG15LnN1YnNldFtbaV1dKQp9CgojIFRha2Ugb25seSBnZW5lcyB0aGF0IGFyZSBwcmVzZW50IGluIHRoZSBhbGwgc2FtcGxlcwpteS5kY3MgPSBsaXN0KCkKCiMgV2UgZ28gdHJob3VnaCBlYWNoIFdHQ05BIGRhdGEKZm9yKGkgaW4gMTpsZW5ndGgoV0dDTkFfZGF0YSkpIHsKICAjV2Ugc3RhcnQgd2l0aCBjb21wbGV0ZSBtb2R1bGVzCiAgbXkuZGMgPSBXR0NOQV9kYXRhW1tpXV0kZHluYW1pY0NvbHMKICAKICAjUmVtb3ZlIHRoZSBmZXcgZ2VuZXMgdGhhdCBhcmUgbm90IGZvdW5kIGluIHRoZSBvdGhlciBkYXRhc2V0cwogIG15LmRjID0gbXkuZGNbd2hpY2gobmFtZXMobXkuZGMpICVpbiUgcm93bmFtZXMobXkuc2VzW1sxXV1AYXNzYXlzJFJOQUBkYXRhKSldCiAgbXkuZGMgPSBteS5kY1t3aGljaChuYW1lcyhteS5kYykgJWluJSByb3duYW1lcyhteS5zZXNbWzJdXUBhc3NheXMkUk5BQGRhdGEpKV0KICBteS5kYyA9IG15LmRjW3doaWNoKG5hbWVzKG15LmRjKSAlaW4lIHJvd25hbWVzKG15LnNlc1tbM11dQGFzc2F5cyRSTkFAZGF0YSkpXQogIAogIG15LmRjID0gbXkuZGNbd2hpY2gobXkuZGMgJWluJSBuYW1lcyh0YWJsZShXR0NOQV9kYXRhW1tpXV0kZHluYW1pY0NvbHMpKVtteS53c3ViW1tpXV1dKV0KICAKICBteS5kY3NbW2ldXSA9IG15LmRjCiAgCn0KYGBgCgojIE1vZHVsZSBnZW5lIGNvcnJlbGF0aW9uCgpIZXJlLCB3ZSBkbyBhIGNvcnJlbGF0aW9uIG1hdHJpeCAvIGhlYXRtYXAsIHRvIHNlZSB3aGljaCBjZWxsIGNsdXN0ZXJzIGdyb3VwIHRvZ2hldGVyLiBUaGlzIGhlbHBzIHVzIHRvIG1ha2UgbW9yZSBkZXRhaWxlZCBkb3RwbG90cy4gIApUaGlzIHBhcnQgb2YgdGhlIHNjcmlwdCBjYW4gc3RpbGwgYmUgdXNlZCB0byBjb21wYXJlIHNldmVyYWwgV0dDTkEgZGF0YXNldHMgaW4gcGFyYWxsZWwuICAKCmBgYHtyfQojIGJyb2FkIGNsdXN0ZXIgY29sb3IgdGFibGUKYWxsX2NvbCA8LSBkby5jYWxsKHJiaW5kLCBjb2xfdGFibGUpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oInNhbXBsZSIpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gc3Vic3RyKHNhbXBsZSwgMSwgMykpICU+JSAKICBtdXRhdGUoc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKSAlPiUgCiAgc2VsZWN0KGMoImNvbG9yIiwgInNhbXBsZV9jZWxsdHlwZSIsICJzYW1wbGUiKSkKYGBgCgpgYGB7ciBtb2R1bGVfZXhwcmVzc2lvbl9kYXRhLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0yMH0KCiMgdGFrZSB0aGUgbm9ybWFsaXplZCBkYXRhLCBvZiBlYWNoIHNpbmdsZS1jZWxsIG9iamVjdC4KbXkuZGF0RXhwciA9IGxpc3QoKQoKIyBHbyB0cm91Z2ggZWFjaCBzZXVyYXQgb2JqZWN0CmZvciAoaSBpbiAxOmxlbmd0aChteS5zZXMpKSB7CiBteS5kYXRFeHByW1tpXV0gPSBkYXRhLmZyYW1lKG15LnNlc1tbaV1dQGFzc2F5cyRSTkFAZGF0YSkKfQoKIyBDYWxjdWxhdGUsIGZvciBldmVyeSBjZWxsLCBpbiBldmVyeSBzYW1wbGUsIHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBvZiB0aGUgbW9kdWxlcy4KbXkuTUVzID0gbGlzdCgpCgojIEZvciBlYWNoIHNldCBvZiBXR0NOQSBtb2R1bGVzCmZvciAoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogIAogICMgTWFrZSBhIG5ldyBsaXN0CiAgbXkuTUVzW1tpXV0gPSBsaXN0KCkKICAKICAjIFBvcHVsYXRlIHdpdGggZGF0YSBmcmFtZXMgZnJvbSBlYWNoIHNldXJhdCBvYmplY3QKICBmb3IgKGogaW4gMTpsZW5ndGgobXkuc2VzKSkgewogICAgCiAgICAjIERhdGEgZnJhbWUgb2YgYXZlcmFnZSBtb2R1bGUgZXhwcmVzc2lvbiwgcGVyIGNlbGwuCiAgICBteS5NRXNbW2ldXVtbal1dID0gbW9kdWxlRWlnZW5nZW5lcyh0KG15LmRhdEV4cHJbW2pdXVtuYW1lcyhteS5kY3NbW2ldXSksXSksIGNvbG9ycyA9IG15LmRjc1tbaV1dKQogIH0KfQoKI0NhbGN1bGF0ZSBhdmVyYWdlIG9mIGV4cHJlc3Npb24sIHBlciBzYW1wbGUgYW5kIGNlbGwgY2x1c3RlcgpteS5tb2RhdmcgPSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogIAogIG15Lm1vZGF2Z1tbaV1dID0gbGlzdCgpCiAgCiAgZm9yIChqIGluIDE6bGVuZ3RoKG15LnNlcykpIHsKICAgIAogICAgI01ha2UgdGhlIG1lYW4gcGVyIGNlbGwgY2x1c3RlcnMKICAgIG15Lm1vZGF2Z1tbaV1dW1tqXV0gPSBhZ2dyZWdhdGUoCiAgICAgIG15Lk1Fc1tbaV1dW1tqXV0kYXZlcmFnZUV4cHIsCiAgICAgIGxpc3QobXkuc2VzW1tqXV1AbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycyksCiAgICAgIG1lYW4KICAgICAgKQogICAgIyBHaXZlIHRoZSBjZWxsIGNsdXN0ZXJzIG1lYW5pbmdmdWwgbmFtZXMKICAgIHJvd25hbWVzKG15Lm1vZGF2Z1tbaV1dW1tqXV0pID0gcGFzdGUwKAogICAgICBteS5zYW1wbGVuYW1lc1tqXSwKICAgICAgIl8iLAogICAgICBsZXZlbHMobXkuc2VzW1tqXV1AbWV0YS5kYXRhJGZpbmUpW215LnN1YnNldFtbal1dXQogICAgICApCiAgICB9Cn0KCgoKI0dhdGhlciBkYXRhIHRvIHBsb3QKCm15LndnbWF0ID0gbGlzdCgpCgpmb3IgKGkgaW4gMTpsZW5ndGgoV0dDTkFfZGF0YSkpIHsKICAKICAjYmluZCB0aGUgZXhwcmVzc2lvbiBkYXRhIGludG8gb25lIGRhdGFmcmFtZQogIG15LndnbWF0W1tpXV0gPSBkYXRhLnRhYmxlOjpyYmluZGxpc3QobXkubW9kYXZnW1tpXV0pCiAgI0dldCByaWQgb2YgdGhlIGV4dHJhIGNvbHVtbgogIG15LndnbWF0W1tpXV0gPSBkYXRhLmZyYW1lKG15LndnbWF0W1tpXV1bLC0xXSkKICAjUmVzdG9yZSB0aGUgcm93bmFtZXMKICByb3duYW1lcyhteS53Z21hdFtbaV1dKSA9IHVubGlzdChzYXBwbHkobXkubW9kYXZnW1tpXV0sIHJvd25hbWVzKSkKICAKfQoKCgoKI0dldCBhIGRhdGFmcmFtZSB3aXRoIGFubm90YXRpb25zIGZvciBhbGwgdGhlIHNhbXBsZXMgYW5kIGNvbG9ycyB3ZSBuZWVkLgpteS5tZXRhbSA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIG15Lm1ldGFtW1tpXV0gPC0gbXkuc2VzW1tpXV1bW11dCiAgcm93bmFtZXMobXkubWV0YW1bW2ldXSkgPC0gcGFzdGUwKHJvd25hbWVzKG15Lm1ldGFtW1tpXV0pLCAiXyIsIGkpCn0KbXkubWV0YW0gPC0gZG8uY2FsbChyYmluZCwgbXkubWV0YW0pCgpteS5tZXRhbSRvcmlnLmlkZW50IDwtIHN0cl9yZXBsYWNlX2FsbChteS5tZXRhbSRvcmlnLmlkZW50LCBwYXR0ZXJuID0gICJHZ19EMDVfY3RybCIsICJEMDUiKQpteS5tZXRhbSRvcmlnLmlkZW50IDwtIHN0cl9yZXBsYWNlX2FsbChteS5tZXRhbSRvcmlnLmlkZW50LCBwYXR0ZXJuID0gICJHZ19EMDdfY3RybCIsICJEMDciKQpteS5tZXRhbSRvcmlnLmlkZW50IDwtIHN0cl9yZXBsYWNlX2FsbChteS5tZXRhbSRvcmlnLmlkZW50LCBwYXR0ZXJuID0gICJHZ19jdHJsXzEiLCAiRDEwIikKCiMgbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKHN1YnN0cihteS5tZXRhbSRvcmlnLmlkZW50LDcsOSksIl8iLG15Lm1ldGFtJHNldXJhdF9jbHVzdGVycykKbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKG15Lm1ldGFtJG9yaWcuaWRlbnQsICJfIiwgbXkubWV0YW0kZmluZSkKCm15Lm1ldGFtIDwtIG15Lm1ldGFtICU+JSAKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbF9JRCIpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oYWxsX2NvbCwgYnkgPSAic2FtcGxlX2NlbGx0eXBlIikgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImNlbGxfSUQiKQoKCiMgZ2V0IHNhbXBsZSBjb2xvcnMKbXkuY29sc20gPSBjKCJncmV5IiwgImdyZXkzMCIsICJibGFjayIpCm5hbWVzKG15LmNvbHNtKSA8LSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpCiAKI1RoZSBjb2xvcnMgZm9yIHRoZSBzYW1wbGVzIGFuZCBjbHVzdGVycy4gRmlyc3QgdGhlIGNsb3Nlc3QgdG8gdGhlIGhlYXRtYXAuIEFkZCBhIHdoaXRlIHNwYWNlIHRvIGVhc3kgdGhlIGV5ZSBhbmQgbWFrZSBsZXNzIGNvbmZ1c3NpbmcKbXkuaGVhdGNvbHMgPWxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgobXkud2dtYXQpKSB7CiAgbXkuaGVhdGNvbHNbW2ldXSA9IGFzLm1hdHJpeChkYXRhLmZyYW1lKAogICAgc2FtcGxlPSBhcy5jaGFyYWN0ZXIobXkuY29sc21bbWF0Y2goYWxsX2NvbCRzYW1wbGUsIG5hbWVzKG15LmNvbHNtKSldKSwKICAgICIuIiA9ICJ3aGl0ZSIsCiAgICBjbHVzdGVyPSBhcy5jaGFyYWN0ZXIoYWxsX2NvbCRjb2xvclttYXRjaChyb3duYW1lcyhteS53Z21hdFtbaV1dKSwgYWxsX2NvbCRzYW1wbGVfY2VsbHR5cGUpXSkpCiAgICApCn0KCnJtKG15LmRhdEV4cHIsIG15Lk1FcywgbXkuZGNzKQpgYGAKCiMjIHNwZWFybWFuIGNvcnJlbGF0aW9uIGhlYXRtYXAKCiMjIyBhbm5vdGF0aW9ucwoKYGBge3IgYW5ub3QtbGlzdH0KCiMgbmFtZXMgYW5kIGNvbG9ycyBmb3IgdGhlIGhlYXRtYXAgYW5ub3RhdGlvbgphbm5vdF9uYW1lIDwtIGRhdGEuZnJhbWUoCiAgIkNlbGx0eXBlcyIgPSBhbGxfY29sJHNhbXBsZV9jZWxsdHlwZSwKICAiU2FtcGxlIiAgICA9IGFsbF9jb2wkc2FtcGxlLAogIHJvdy5uYW1lcyA9IGFsbF9jb2wkc2FtcGxlX2NlbGx0eXBlKQogIAoKcGhlYXRfY29sX3RhYmxlIDwtIGRvLmNhbGwocmJpbmQsIGNvbF90YWJsZSkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigic2FtcGxlIikgJT4lIAogIG11dGF0ZShzYW1wbGUgPSBzdWJzdHIoc2FtcGxlLCAxLDMpKSAlPiUgCiAgbXV0YXRlKGZpbmUgPSBwYXN0ZShzYW1wbGUsIGZpbmUsIHNlcCA9ICJfIikpCgojIG1hdGNoIGNvbG9yIHRhYmxlIHdpdGggYW5ub3RhdGlvbgpwaGVhdF9jb2xfdGFibGUgPC0gcGhlYXRfY29sX3RhYmxlW21hdGNoKGFubm90X25hbWUkQ2VsbHR5cGVzLCBwaGVhdF9jb2xfdGFibGUkZmluZSksXQoKYW5ub3RfY29sIDwtIGxpc3QoCiAgQ2VsbHR5cGVzID0gcGhlYXRfY29sX3RhYmxlJGNvbG9yLAogIFNhbXBsZSA9IGMoRDA1ID0gIiNBNEE0QTQiLAogICAgICAgICAgICAgRDA3ID0gIiM1MTUxNTEiLAogICAgICAgICAgICAgRDEwID0gIiMwMDAwMDAiKQogICkKCm5hbWVzKGFubm90X2NvbFtbMV1dKSA8LSBhbm5vdF9uYW1lJENlbGx0eXBlcwoKYGBgCgpgYGB7ciwgIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xM30KaGVhdF9jb2wgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJkb2RnZXJibHVlNCIsImRvZGdlcmJsdWUiLCAid2hpdGUiLCAicmVkIiwgImRhcmtyZWQiKSkKCiNDYWxjdWxhdGUgdGhlIGRpc3RhbmNlIDEtY29yLCBhbmQgdGhlbiBjYWxjdWxhdGUgZGVuZG9ncmFtcyB0byBjbHVzdGVyIHRoZSBjbHVzdGVycwpteS5oY2x1c3QgPSBoY2x1c3QoYXMuZGlzdCgxLWNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSkpCgojIGxvd2VyIGxpbWl0IHRvIHNjYWxlIGNvbG9yIGJhcgpsb3dfbGltaXQgPC0gMTAwIC0gYWJzKHJvdW5kKHJhbmdlKGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSlbMV0qMTAwKSkKCnBoZWF0bWFwKGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSxyZXZDID0gVCwKICAgICAgICAgICAgICAgICBtYWluID0gInNwZWFybWFuIGNvcnJlbGF0aW9uIG9mIGF2ZXJhZ2UgbW9kdWxlIGVpZ2VuZ2VuZSBleHByZXNzaW9uIGJ5IGNsdXN0ZXJcbiBEZW5kcm9ncmFtIGJhc2VkIG9uICdkaXN0YW5jZSAxLWNvciciLAogICAgICAgICAgICAgICAgIGZvbnRzaXplID0gOCwgIAogICAgICAgICAgICAgICAgIGNvbG9yID0gaGVhdF9jb2woMjAwKVtsb3dfbGltaXQ6MjAwXSwKICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBteS5oY2x1c3QsCiAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gbXkuaGNsdXN0LAogICAgICAgICAgICAgICAgIHRyZWVoZWlnaHRfcm93ID0gMCwgCiAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBhbm5vdF9uYW1lLAogICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5ub3RfY29sLAogICAgICAgICAgICAgICAgIGFubm90YXRpb25fbGVnZW5kID0gRiwKICAgICAgICAgICAgICAgICBib3JkZXJfY29sb3IgPSBOQQogICAgICAgICAgICAgICAgICkKCmNvbGJhciA8LSBjKG1pbihjb3IodChteS53Z21hdFtbMV1dKSwgbWV0aG9kID0gInNwZWFybWFuIikpLAogICAgICAgICAgICBtYXgoY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkKCiNDYWxjdWxhdGUgdGhlIGRpc3RhbmNlIDEtY29yLCBhbmQgdGhlbiBjYWxjdWxhdGUgZGVuZG9ncmFtcyB0byBjbHVzdGVyIHRoZSBjbHVzdGVycwpteS5oY2x1c3QgPSBhcy5kZW5kcm9ncmFtKGhjbHVzdChhcy5kaXN0KDEtY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkpCgojUGxvdCEKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfbW9kdWxlX2dlbmVfaGVhdG1hcF9zcGVhcm1hbi5wZGYiLCB3aWR0aCA9IDExLCBoZWlnaHQgPSAxMSkKaGVhdG1hcC40KGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpKG4gPSAxMDAwKSwKICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgIGxoZWk9YygwLjIsMS42KSwKICAgICAgICAgIGx3aWQ9YygwLjIsMS42KSwKICAgICAgICAgIHNjYWxlID0gIm5vbmUiLG1hcmdpbnMgPSBjKDEwLDEwKSwKICAgICAgICAgIGNleENvbCA9IDAuNzUsCiAgICAgICAgICByZXZDID0gVFJVRSwKICAgICAgICAgIENvbFNpZGVDb2xvcnMgPSBteS5oZWF0Y29sc1tbMV1dLAogICAgICAgICAgUm93U2lkZUNvbG9ycyA9IHQobXkuaGVhdGNvbHNbWzFdXVssMzoxXSksUm93diA9IG15LmhjbHVzdCwgQ29sdiA9IG15LmhjbHVzdCkKCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9tb2R1bGVfZ2VuZV9oZWF0bWFwX2NvbG9yX2tleV9zcGVhcm1hbi5wZGYiLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCkKIyBoZWF0bWFwIHNpbmNlIGNvbG9yIGtleSBpcyBtaXNzaW5nIGluIGZpcnN0IGhlYXRtYXAKaGVhdG1hcC40KHJiaW5kKGNvbGJhciwgY29sYmFyKSwKICAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpKG4gPSAxMDAwKSwKICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgIGxoZWk9YygwLjIsMS42KSwKICAgICAgICAgIGx3aWQ9YygwLjIsMS42KSwKICAgICAgICAgIHNjYWxlID0gIm5vbmUiLG1hcmdpbnMgPSBjKDEwLDEwKSwKICAgICAgICAgIGNleENvbCA9IDAuNzUpCgpgYGAKCiMjIGhlYXRtYXAgb2YgbW9kdWxlIHBzZXVkb2J1bGsgYXZlcmFnZSBleHByZXNzaW9uCgpgYGB7ciwgIGZpZy5oZWlnaHQ9MTUsIGZpZy53aWR0aD0xMH0KIyBtb2R1bGUgY29sb3JzCm15LmNvbGNvbHMgPSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChteS53Z21hdCkpIHsKICAgIG15LmNvbGNvbHNbW2ldXSA9IGFzLm1hdHJpeChuYW1lcyh0YWJsZShXR0NOQV9kYXRhW1tpXV0kZHluYW1pY0NvbHMpKSkKfQoKYXZnLm1vZC5laWdlbmdlbmVzIDwtIFdHQ05BX2RhdGFbWzFdXSRzYy5NRUxpc3QkYXZlcmFnZUV4cHIKcm0oV0dDTkFfZGF0YSkKCmh0bXAgPC0gaGVhdG1hcC40KGFzLm1hdHJpeChteS53Z21hdFtbMV1dKSwKICAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpKG4gPSAxMDAwKSwKICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgIHNjYWxlID0gInJvdyIsCiAgICAgICAgICBtYXJnaW5zID0gYygxMCwxMCksCiAgICAgICAgICBDb2xTaWRlQ29sb3JzID0gbXkuY29sY29sc1tbMV1dLCAKICAgICAgICAgIFJvd1NpZGVDb2xvcnMgPSB0KG15LmhlYXRjb2xzW1sxXV0pKQoKbW9kdWxlX29yZGVyIDwtIGh0bXBbWyJjb2xJbmQiXV0KCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0RldmVsX21vZHVsZV92X2NsdXN0ZXJzX2hlYXRtYXAucGRmIiwgd2lkdGggPSA3LCBoZWlnaHQgPSAxMCkKaGVhdG1hcC40KGFzLm1hdHJpeChteS53Z21hdFtbMV1dKSwKICAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpKG4gPSAxMDAwKSwKICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgIHNjYWxlID0gInJvdyIsCiAgICAgICAgICBtYXJnaW5zID0gYygxMCwxMCksCiAgICAgICAgICBDb2xTaWRlQ29sb3JzID0gbXkuY29sY29sc1tbMV1dLCAKICAgICAgICAgIFJvd1NpZGVDb2xvcnMgPSB0KG15LmhlYXRjb2xzW1sxXV0pKQoKYGBgCgoKCiMgTG9hZCB0aGUgaW50ZWdyYXRlZCBkYXRhIHNldAoKVGhlIGludGVncmF0ZWQgZGF0YSBzZXQgb24gd2hpY2ggdGhlIFdHQ05BIGlzIGNhbGN1bGF0ZWQuIFdlIHBsb3QgaXQsIHNwbGl0IGJ5IHRoZSBvcmlnaW5hbCBjZWxsIHR5cGVzIGZyb20gdGhlIHRocmVlIG9yaWdpbmFsIHNhbXBsZXMuIAoKYGBge3IgZGltcGxvdHMsIGZpZy53aWR0aD0xMH0KIyBUaGlzIGlzIGEgZmlsZSBvZiBhbGwgdGhlIGNvbWJpbmVkIG1vdXNlIGRhdGFzZXRzLCBub3JtYWxpemVkIGFuZCBzdWNoLgpteS5zZWMgPSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvR2dfZGV2ZWxfaW50X3NldXJhdF8yNTA3MjMucmRzIikKCmlkZW50aWNhbChyb3duYW1lcyhteS5tZXRhbSksIGNvbG5hbWVzKG15LnNlYykpCgojV2hpY2ggV0dDTkEgZGF0YXNldCBhcmUgd2UgdXNpbmc/Cm15LlcgPSAxCgojQ2hvb3NlIGhvdyBtYW55IGdyb3VwcyBvZiBjZWxsIGNsdXN0ZXJzIHdlIHdhbnQgdG8gdXNlLiBCYXNlZCBvbiB0aGUgZGlzdGFuY2UgY2x1c3RlcmluZyBmcm9tIGFib3ZlCm15LmNsY2wgPSBjdXRyZWUoaGNsdXN0KGFzLmRpc3QoMS1jb3IodChteS53Z21hdFtbbXkuV11dKSkpKSwgayA9IDUpCgpteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUgPC0gZmFjdG9yKG15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSwgbGV2ZWxzID0gbmFtZXMobXkuY2xjbCkpCiNTZXQgdGhlIGlkZW50aXRpZXMgb2YgdGhlIGludGVncmF0ZWQgZGF0YSwgdG8gdGhlIGFubm90YXRlZCBjbHVzdGVycwpteS5zZWMgPSBTZXRJZGVudChteS5zZWMsIHZhbHVlID0gbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlKQoKcDEgPC0gRGltUGxvdCgKICBteS5zZWMsCiAgcmVkdWN0aW9uID0gInRzbmUiLAogIGxhYmVsID0gVFJVRSwKICByZXBlbCA9IFRSVUUsCiAgY29scyA9IG15LmhlYXRjb2xzW1sxXV1bLCJjbHVzdGVyIl0sCiAgc3BsaXQuYnkgPSAib3JpZy5pZGVudCIKICApICsgCiAgTm9MZWdlbmQoKQoKcDEKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0RldmVsX3NwbGl0X3RzbmUucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSAxNSkKI1Bsb3Qgc3BsaXQgdHNuZQpwMQpgYGAKCiMgQXZnLiBtb2R1bGUgZXhwLiBieSBzdGFnZQoKdFNORSBEaW1QbG90cyBzaG93aW5nIHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBtb2R1bGUgYnkgc3RhZ2UuCgpgYGB7ciBBRSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTQwfQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgIyBwcmVwYXJlIGF2ZXJhZ2UgZXhwcmVzc2lvbiB0YWJsZQogIHRtcCA8LSBhdmcubW9kLmVpZ2VuZ2VuZXMgJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JQogICAgZHBseXI6OmZpbHRlcihncmVwbChwYXN0ZTAoIl8iLCBpLCAiJCIpLCBjZWxsX0lEKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNlbGxfSUQgPSBzdHJpbmdyOjpzdHJfcmVtb3ZlX2FsbChjZWxsX0lELCBwYXN0ZTAoIl8iLCBpKSkpICU+JQogICAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoImNlbGxfSUQiKQogIAogIGlkZW50aWNhbChyb3duYW1lcyh0bXApLCBjb2xuYW1lcyhteS5zZXNbW2ldXSkpCiAgIyBhZGQgbWV0YSBkYXRhIHRvIHRoZSBzZXVyYXQgb2JqZWN0cwogIG15LnNlc1tbaV1dIDwtIEFkZE1ldGFEYXRhKG15LnNlc1tbaV1dLCB0bXApCn0KCiNtYXggYW5kIG1pbiBleHByZXNzaW9uIHBlciBtb2R1bGUgKGNvbHVtbiBtYXgpCm1vZF9tYXggPC0gYXBwbHkoYXZnLm1vZC5laWdlbmdlbmVzLCBNQVJHSU4gPSAyLCBGVU4gPSBtYXgpW21vZHVsZV9vcmRlcl0KbW9kX21pbiA8LSBhcHBseShhdmcubW9kLmVpZ2VuZ2VuZXMsIE1BUkdJTiA9IDIsIEZVTiA9IG1pbilbbW9kdWxlX29yZGVyXQoKbW9kcGxvdHMgPC0gbGlzdCgpCm1vZHBsb3RzW1sxXV0gPC0gbGlzdCgpCm1vZHBsb3RzW1syXV0gPC0gbGlzdCgpCm1vZHBsb3RzW1szXV0gPC0gbGlzdCgpCgptb2R1bGVzX2luX29yZGVyIDwtIGNvbG5hbWVzKHRtcClbbW9kdWxlX29yZGVyXQoKIyBwbG90IHRoZSBtb2R1bGVzIHNwbGl0IHRvIHRoZSBzdGFnZXMKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgZm9yIChqIGluIHNlcShuY29sKHRtcCkpKSB7CiAgCiAgICBtb2RwbG90c1tbaV1dW1tqXV0gIDwtIEZlYXR1cmVQbG90KAogICAgICBteS5zZXNbW2ldXSwKICAgICAgZmVhdHVyZXMgPSBtb2R1bGVzX2luX29yZGVyW2pdLAogICAgICByZWR1Y3Rpb24gPSAidHNuZSIsCiAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCBzdWJzdHJpbmcobW9kdWxlc19pbl9vcmRlcltqXSwgMykpCiAgICAgICkgKwogICAgICBnZ3RpdGxlKHN0cmluZ3I6OnN0cl9yZW1vdmUobW9kdWxlc19pbl9vcmRlcltqXSwiXkFFIikpICsKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93PSJpdm9yeTIiLCBoaWdoPXN1YnN0cmluZyhtb2R1bGVzX2luX29yZGVyW2pdLCAzKSwgI2NvbG9ycyBpbiB0aGUgc2NhbGUKICAgICAgICAgICAgICAgICAjIGJyZWFrcz1zZXEobW9kX21pbltqXSwgbW9kX21heFtqXSwgMC4xKSwgI2JyZWFrcyBpbiB0aGUgc2NhbGUgYmFyCiAgICAgICAgICAgICAgICAgbGltaXRzPWMobW9kX21pbltqXSwgbW9kX21heFtqXSkpICNzYW1lIGxpbWl0cyBmb3IgcGxvdHMKCiAgICAKICAgIH0KfQoKdG1wIDwtIGMobW9kcGxvdHNbWzFdXSwgbW9kcGxvdHNbWzJdXSwgbW9kcGxvdHNbWzNdXSkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gdG1wLCBuY29sID0gMywgYXMudGFibGUgPSBGQUxTRSkKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0RldmVsX21vZHVsZXNfQUVfcGxvdHMucGRmIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNzApCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gdG1wLCBuY29sID0gMywgYXMudGFibGUgPSBGQUxTRSkKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX01OX21vZF90c25lLnBkZiIsIGhlaWdodCA9IDYsIHdpZHRoID0gMTIpCm1vZHBsb3RzW1sxXV1bWzIyXV0gKyBtb2RwbG90c1tbMV1dW1s2XV0gfAogIG1vZHBsb3RzW1syXV1bWzIyXV0gKyBtb2RwbG90c1tbMl1dW1s2XV0gfAogIG1vZHBsb3RzW1szXV1bWzIyXV0gKyBtb2RwbG90c1tbM11dW1s2XV0KCmBgYAoKIyBBRSBvdmVyIHRpbWUKCldlIHBsb3QgdGhlIGF2ZXJhZ2UgZXhwcmVzc2lvbiBvZiBlYWNoIG1vZHVsZSBpbiB0aGUgdGhyZWUgc3RhZ2VzIGFuZCB0aGUgNSBicm9hZCBjZWxsIHR5cGUgY2x1c3RlcnMgcHJlc2VudCBpbiBhbGwgMyBzdGFnZXMuCgpgYGB7ciBBRS1vdmVyLXRpbWUsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEyfQojIG1vZHVsZSBhbm5vdGF0aW9ucwptb2RfYW5ub3QgPC0gcmVhZC5jc3YoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMvR2dfZGV2ZWxfaW50X3NjV0dDTkFfbW9kdWxlX2Fubm90YXRpb24uY3N2IikgJT4lCiAgZHBseXI6Om11dGF0ZShtb2R1bGUgPSBzdHJfcmVwbGFjZV9hbGwobW9kdWxlLCAiXFxkezEsMn1cXF8iLCAiQUUiKSkKCm1ldGEgPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBtZXRhW1tpXV0gPC0gbXkuc2VzW1tpXV1AbWV0YS5kYXRhICU+JQogICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImNlbGxfSUQiKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoY2VsbF9JRCA9IHBhc3RlMChjZWxsX0lELCAiXyIsIGkpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoYygiY2VsbF9JRCIsICJicm9hZCIpKQp9CgptZXRhIDwtIGRvLmNhbGwocmJpbmQsIG1ldGEpCgojIG1lYW4gYXZlcmFnZSBleHByZXNzaW9uIGJ5IHN0YWdlCm1lYW5fQUUgPC0gYXZnLm1vZC5laWdlbmdlbmVzICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lCiAgZHBseXI6Om11dGF0ZShzdGFnZSA9IHN0cmluZ3I6OnN0cl9zdWIoY2VsbF9JRCwgLTEpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHN0YWdlID0gZmFjdG9yKHN0YWdlLCBsZXZlbHMgPSBjKDE6MyksIGxhYmVscyA9IGMoIkQwNSIsICJEMDciLCAiRDEwIikpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKG1ldGEsIGJ5ID0gImNlbGxfSUQiKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygiY2VsbF9JRCIpICU+JQogIHRpZHlyOjp1bml0ZSgic3RhZ2VfY2wiLCBzdGFnZSwgYnJvYWQsIHNlcCA9ICJfIikgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHN0YWdlX2NsKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlX2VhY2gobWVhbikgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBnYXRoZXIoa2V5PSJtb2R1bGUiLCB2YWx1ZSA9ICJBRSIsIC1zdGFnZV9jbCkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihtb2RfYW5ub3RbLCBjKDEsMyldLCBieSA9ICJtb2R1bGUiKSAlPiUKICB0aWR5cjo6c2VwYXJhdGUoInN0YWdlX2NsIiwgYygic3RhZ2UiLCAiYnJvYWQiKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgCiMgJT4lCiAgIyBkcGx5cjo6ZmlsdGVyKGJyb2FkICVpbiUgYygicHJvZ2VuaXRvcnMiLCAibmV1cm9ucyIsICJSUCIsICJGUCIsICJwZXJpY3l0ZXMiKSkKCmxhYmVsc19kb3RwbG90IDwtIHN0cmluZ3I6OnN0cl9yZW1vdmUobW9kdWxlc19pbl9vcmRlciwgIl5BRSIpCm5hbWVzKGxhYmVsc19kb3RwbG90KSA8LSBtb2R1bGVzX2luX29yZGVyCgpwbG90X2NsdXN0ZXJzIDwtIGMoInByb2dlbml0b3JzIiwgIm5ldXJvbnMiLCAiUlAiLCAiRlAiLCAicGVyaWN5dGVzIiwgIk9QQyIsICJNRk9MIiwgIm1pY3JvZ2xpYSIsICJibG9vZCIpCgptZWFuX21vZCA8LSBnZ3Bsb3QoZGF0YSA9IG1lYW5fQUUsCiAgYWVzKAogICAgeCA9IHN0YWdlLAogICAgeSA9IEFFLAogICAgY29sb3IgPSBmYWN0b3IoYnJvYWQsIGxldmVscyA9IHBsb3RfY2x1c3RlcnMpLAogICAgZ3JvdXAgPSBicm9hZCwKICAgIGxhYmVsID0gYW5ub3RhdGlvbgogICAgKQogICkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjbHVzdF9jb2wkY29sb3JbbWF0Y2gocGxvdF9jbHVzdGVycywgY2x1c3RfY29sJGJyb2FkKV0pICsKICB0aGVtZV9idygpICsKICAjIGZhY2V0IHdyYXAgd2l0aCByZW9yZGVyZWQgZmFjdG9ycwogIGZhY2V0X3dyYXAodmFycyhmYWN0b3IobW9kdWxlLCBsZXZlbHMgPSB1bmlxdWUobWVhbl9BRSRtb2R1bGUpW21vZHVsZV9vcmRlcl0pKSwKICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlX3kiLAogICAgICAgICAgICAgbnJvdyA9IDQsCiAgICAgICAgICAgICBuY29sID0gNikgKwogIGxhYnMoY29sb3IgPSAiYnJvYWQiKSArCiAgZ2d0aXRsZSgiQXZlcmFnZSBtb2R1bGUgZXhwcmVzc2lvbiBieSBzdGFnZSIpCgpwbG90bHk6OmdncGxvdGx5KG1lYW5fbW9kKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfbWVhbl9tb2RfQUUucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSAxMikKI1Bsb3Qgc3BsaXQgdHNuZQptZWFuX21vZApgYGAKCiMgVmxuUGxvdHMgb2YgYXZnLiBtb2R1bGUgZXhwLiBieSBzdGFnZSBhbmQgc2V1cmF0IGNsdXN0ZXIgCgojIyBjb2xvcmVkIGJ5IG1vZHVsZQoKYGBge3IgYXZlcmFnZS1tb2R1bGUtZXhwcmVzc2lvbi1wZXItY2x1c3RlciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTIwfQoKIyByZW9yZGVyIHNldXJhdCBjbHVzdGVycwpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBteS5zZXNbW2ldXSRzZXVyYXRfY2x1c3RlcnMgPC0gZmFjdG9yKAogIG15LnNlc1tbaV1dJHNldXJhdF9jbHVzdGVycywKICBsZXZlbHMgPSBsZXZlbHMobXkuc2VzW1tpXV0kc2V1cmF0X2NsdXN0ZXJzKVthcy5pbnRlZ2VyKHN0cl9leHRyYWN0KG9yZF9sZXZlbHNbW2ldXSwgIlxcZHsxLDJ9JCIpKV0KICApCgp9Cgp2cGxvdHMgPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICAKICBtb2RzIDwtIGNvbG5hbWVzKG15LnNlc1tbaV1dW1tdXSlbZ3JlcCgiXkFFIixjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pKV0KICAKICB2cGxvdHNbW2ldXSA8LSBWbG5QbG90KAogICAgICAgIG15LnNlc1tbaV1dLAogICAgICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsCiAgICAgICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgICAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCiAgCn0KCnZwbG90c1tbMV1dCnZwbG90c1tbMl1dCnZwbG90c1tbM11dCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9BRV9ieV9jbHVzdGVyX21vZGNvbC5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAxMCkKdnBsb3RzW1sxXV0KdnBsb3RzW1syXV0KdnBsb3RzW1szXV0KYGBgCgojIyBjb2xvcmVkIGJ5IGNlbGwgdHlwZQoKYGBge3IgdmxuLWJ5LWNsdXN0ZXIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0yMH0KY2x1c3RfY29sIDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL2Jyb2FkX2NsdXN0ZXJfbWFya2VyX2NvbG9ycy5jc3YiKQoKdnBsb3RzX2lkIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgICAKICBtb2RzIDwtIGNvbG5hbWVzKG15LnNlc1tbaV1dW1tdXSlbZ3JlcCgiXkFFIixjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pKV0KICAKICB2cGxvdHNfaWRbW2ldXSA8LSBWbG5QbG90KAogICAgbXkuc2VzW1tpXV0sCiAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsCiAgICBzdGFjayA9IFRSVUUsIGZsaXAgPSBUUlVFLAogICAgZmlsbC5ieSA9ICJpZGVudCIsCiAgICBjb2xzID0gY29sX3RhYmxlW1tpXV0kY29sb3JbYXMuaW50ZWdlcihzdHJfZXh0cmFjdChvcmRfbGV2ZWxzW1tpXV0sICJcXGR7MSwyfSQiKSldKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQp9Cgp2cGxvdHNfaWRbWzFdXQp2cGxvdHNfaWRbWzJdXQp2cGxvdHNfaWRbWzNdXQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfQUVfYnlfY2x1c3Rlcl9jbHVjb2wucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMTApCnZwbG90c19pZFtbMV1dCnZwbG90c19pZFtbMl1dCnZwbG90c19pZFtbM11dCmBgYAoKIyBNTiBtb2R1bGVzCgpGb3IgdGhlIGZpZ3VyZXMgd2Ugc3BlY2lmaWNhbGx5IHNlbGVjdCB0aGUgdHdvIE1OIG1vZHVsZXMgYW5kIHBsb3QgdGhlbSBhcyBWbG4gcGxvdHMuCgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTV9CnRtcCA8LSBhdmcubW9kLmVpZ2VuZ2VuZXMgCiAgCmlkZW50aWNhbChyb3duYW1lcyh0bXApLCBjb2xuYW1lcyhteS5zZWMpKQojIGFkZCBtZXRhIGRhdGEgdG8gdGhlIGludCBzZXVyYXQgb2JqZWN0Cm15LnNlYyA8LSBBZGRNZXRhRGF0YShteS5zZWMsIHRtcCkKbXkuc2VjIDwtIEFkZE1ldGFEYXRhKG15LnNlYywgbXkubWV0YW1bYygiZmluZSIsICJzYW1wbGVfY2VsbHR5cGUiKV0pCgpjdXN0b21fb3JkZXIgPC0gYyhwYXN0ZShuYW1lcyhvcmRfbGV2ZWxzKVsxXSwgb3JkX2xldmVsc1tbMV1dLCBzZXAgPSAnXycpLAogICAgICAgICAgICAgICAgICBwYXN0ZShuYW1lcyhvcmRfbGV2ZWxzKVsyXSwgb3JkX2xldmVsc1tbMl1dLCBzZXAgPSAnXycpLAogICAgICAgICAgICAgICAgICBwYXN0ZShuYW1lcyhvcmRfbGV2ZWxzKVszXSwgb3JkX2xldmVsc1tbM11dLCBzZXAgPSAnXycpKQoKbXkuc2VjJHNhbXBsZV9jZWxsdHlwZSA8LSBmYWN0b3IoCiAgbXkuc2VjJHNhbXBsZV9jZWxsdHlwZSwKICBsZXZlbHMgPSBjdXN0b21fb3JkZXIKICApCgp2bG5faW5kIDwtIFZsblBsb3QobXkuc2VjLAogICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJBRWRhcmtyZWQiLCAiQUVncmVlbiIpLAogICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgICAgICAgICAgICAgICAgIHN0YWNrID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIGZsaXAgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgY29scyA9IGMoImRhcmtyZWQiLCAiZ3JlZW4iKSxwdC5zaXplID0gMQogICAgICAgICAgICAgICAgICApICsKICAgIE5vTGVnZW5kKCkKCnZsbl9pbmQKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfQUVfTU5zLnBkZiIsIGhlaWdodCA9IDcsIHdpZHRoID0gMjApCnZsbl9pbmQKYGBgCgpgYGB7cn0KVmxuUGxvdCgKICAgIG15LnNlYywKICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCgoKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX0FFX2J5X2NsdXN0ZXJfaW50ZWdyYXRlZF9kYXRhLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDMwKQpWbG5QbG90KAogICAgbXkuc2VjLAogICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sCiAgICBncm91cC5ieSA9ICJzYW1wbGVfY2VsbHR5cGUiLAogICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgIGNvbHMgPSBzdWJzdHJpbmcobW9kcywgMylbbW9kdWxlX29yZGVyXSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAiZGFzaGVkIikKCgpgYGAKCiMgYXZnLiBtb2R1bGUgZXhwLiBieSBzdGFnZQoKYGBge3IgQUUtYnktc3RhZ2UsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0KdjEgPC0gVmxuUGxvdChteS5zZWMsCiAgICAgICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sIAogICAgICAgIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKQp2MQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfQUVfYnlfc3RhZ2UucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMjApCnYxCgpgYGAKCiMgbW9kdWxlIEdPIHRlcm1zIGJhcnBsb3RzCgpgYGB7cn0KIyBMb2FkIHRoZSB0b3AgNTAgKGJ5IGxpbW1hOjp0b3BHTykgR28gdGVybSB0YWJsZSBsaXN0IGZyb20gc2NXR0NOQV9HZ19kZXZlbF9pbnQuUm1kCmdvdGVybXMgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9vdXRwdXQvR2dfZGV2ZWxfaW50X21vZHVsZV9HT1Rlcm1zXzI1MDcyMy5yZHMiKQojIExvYWQgdGhlIGRldmVsIG1vZHVsZSBhbm5vdGF0aW9ucyAKbW9kdWxlcyA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9HZ19kZXZlbF9pbnRfc2NXR0NOQV9tb2R1bGVfYW5ub3RhdGlvbi5jc3YiKQojIFNldCBsaXN0IG5hbWVzIGFzIG1vZHVsZSBudW1iZXIgYW5kIG5hbWUgKGUuZy4gIjFfYmxhY2siKQpuYW1lcyhnb3Rlcm1zKSA8LSBtb2R1bGVzJG1vZHVsZQoKcGxvdF9saXN0IDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShnb3Rlcm1zKSkgewogICMgU2VsZWN0IHRoZSBkYXJrcmVkIG1vZHVsZSAobW90b3IgbmV1cm9uL3RyYW5zbWVtYnJhbmUgdHJhbnNwb3J0KQogIHRvcGxvdCA8LSBnb3Rlcm1zW1tpXV0KICB0ZXJtcyA8LSBhcy5jaGFyYWN0ZXIodG9wbG90JFRlcm0pCiAgCiAgI1Bsb3QgdGhlIGJhcnBsb3QhCiAgcGxvdF9saXN0W1tpXV0gPC0gZ2dwbG90KHRvcGxvdCwgYWVzKHg9VGVybSwgeT0tbG9nMTAoUC5ERSksIGZpbGw9T250KSkgKwogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBzdHJfcmVtb3ZlKG5hbWVzKGdvdGVybXMpW2ldLCAiXlxcZHsxLDJ9XyIpKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpLAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDUpKSArCiAgICBsYWJzKHRpdGxlPXBhc3RlMCgiVG9wIEdPVGVybSBvZiBtb2R1bGUgIiwgbmFtZXMoZ290ZXJtcylbaV0pKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKwogICAgdGhlbWVfY293cGxvdCgpCn0KCnJtKHRvcGxvdCwgdGVybXMpCgpuYW1lcyhwbG90X2xpc3QpIDwtIG1vZHVsZXMkbW9kdWxlCgojIEV4cG9ydCBQREYKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvR09fdGVybV9iYXJwbG90X3NjV0dDTkFfR2dfZGV2ZWxfbW9kdWxlcy5wZGYiLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSA3KQpmb3IgKGkgaW4gc2VxKGdvdGVybXMpKSB7CiAgZ3JpZC5hcnJhbmdlKHBsb3RfbGlzdFtbaV1dKQp9CmRldi5vZmYoKQoKYGBgCgoKYGBge3J9CiMgRGF0ZSBhbmQgdGltZSBvZiBSZW5kZXJpbmcKU3lzLnRpbWUoKQoKc2Vzc2lvbkluZm8oKQpgYGAKCg==